The C8051F V2.1 is widely available via different channels. The one used here was bought on ebay. I bought it since it was advertized as featuring an C8051F120, which has enough RAM to run Dhrystone, but the board that arrived has a C8051F020 instead. This short tutorial is about a simple LED and timer demo for a C8051F V2.1. I used a Debian GNU/Linux system, but the tutorial should work for other Linux distributions, *BSD or other Unices.
The tools we use are
The C8051F V2.1 board is connected to the host computer via a U-EC6 to write the demo onto the board, while power is supplied via an extra cable.
Depending on your operating system there might be an easy way to install SDCC 3.5.0 or newer using a package system or similar (e.g. apt-get install sdcc on Debian). While SDCC 3.4.0 should be sufficient for this tutorial, you might want to try a newer version in case you encounter any bugs.
SDCC binaries or a source tarball can be downloaded from its website.
ec2writeflash is part of the ec2-new package.
The ec2-new source can be found at its GitHub location, where there is also a download link for a zip archive of the sources. To compile it, a C compiler, such as gcc, autotools and some necessary libraries need to be installed. Unzip the archive (e.g. using unzip stm8flash-master.zip) change into the directory stm8flash-master and type autoreconf; automake --add-missing; libtoolize; autoreconf; ./configure && make
. In case there are any errors, such as header files not found, check that all necessary libraries are installed.
We present a simple Demo that blinks one LED, once per second. This demonstrates setting up and using a timer and doing basic I/O. However, since the demo uses the internal oscillator for simplicity, which on the C8051F020 is not calibrated, the blinking might be up to 20% slower or faster. If you need a more accurate timer on the C8051F020, an external oscillator is necessary and can be configured via OSCXCN
and OSCICN
(see below). Here is the C code:
// Source code under CC0 1.0 #include <stdbool.h> __sfr __at(0xb0) P3; __sfr __at(0xa7) P3MDOUT; __sfr __at(0xe3) XBR2; __sfr __at(0xff) WDTCN; __sfr __at(0x88) TCON; __sfr __at(0x89) TMOD; __sfr __at(0x8a) TL0; __sfr __at(0x8c) TH0; __sfr __at(0xa8) IE; volatile unsigned long int clocktime; volatile bool clockupdate; void clockinc(void) __interrupt(1) { TH0 = (65536 - 167) / 256; TL0 = (65536 - 167) % 256; clocktime++; clockupdate = true; } unsigned long int clock(void) { unsigned long int ctmp; do { clockupdate = false; ctmp = clocktime; } while (clockupdate); return(ctmp); } unsigned char _sdcc_external_startup(void) { // Disable watchdog timer WDTCN = 0xde; WDTCN = 0xad; return 0; // perform normal initialization } void main(void) { // Enable port output XBR2 = 0x40; P3MDOUT = 0x08; // Configure timer for 2 Mhz default SYSCLK // 1000 ticks per second TH0 = (65536 - 167) / 256; TL0 = (65536 - 167) % 256; TMOD = 0x01; IE |= 0x82; TCON |= 0x10; // Start timer for(;;) P3 = ((clock() / 1000) & 0x01) << 3; }
SDCC is a freestanding, not a hosted implemenatation of C, and allows main to return void.
We set up the timer to generate an interupt once per millisecond, which allows us to implement a basic clock()
function. This function is used to control the blinking of the LEDs. The C8051 has a watchdog that is active on startup, and needs to be disabled. To ensure that this happens before the watchdog can reset the device, we disable the watchdog in in _sdcc_external_startup()
which is executed before the initialization of global variables.
The demo can be compiled simply by invocing sdcc using sdcc -mmcs51 --std-c99 led.c
assuming the C code is in led.c. The option -mmcs51
selects the target port (mcs51). An .ihx file with a name corresponding to the source file will be generated.
Assuming the board is connected to a U_EC6 attached via USB, ec2writeflash --port USB --hex led.ihx --run
will write the demo onto the board. It will run and count up to 3 on the red LEDs, then start again at 0.
Using an external crystal, the accuracy of the timers can be improved. This requires some extra setup. Since we use quite some special function registers, we take their locations from the C8051F020.h
header that comes with SDCC instead of placing them ourselves. Here is the C code:
// Source code under CC0 1.0 #include <stdbool.h< #include <C8051F020.h> volatile unsigned long int clocktime; volatile bool clockupdate; volatile bool external_clock; void clockinc(void) __interrupt(1) { if(!external_clock) { TH0 = (65536 - 167) / 256; TL0 = (65536 - 167) % 256; } else { TH0 = (65536 - 1843) / 256; TL0 = (65536 - 1843) % 256; } clocktime++; clockupdate = true; } unsigned long int clock(void) { unsigned long int ctmp; do { clockupdate = false; ctmp = clocktime; } while (clockupdate); return(ctmp); } unsigned char _sdcc_external_startup(void) { // Disable watchdog timer WDTCN = 0xde; WDTCN = 0xad; return 0; // perform normal initialization } void main(void) { unsigned long int oldclock; // Enable port output XBR2 = 0x40; P3MDOUT = 0x08; // Configure timer for 2 Mhz default SYSCLK // 1000 ticks per second external_clock = false; TH0 = (65536 - 167) / 256; TL0 = (65536 - 167) % 256; TMOD = 0x01; IE |= 0x82; TCON |= 0x10; // Start timer // Switch to external crystal oscillator OSCXCN = 0x67; oldclock = clock(); while(clock() - oldclock < 3); while(!(OSCXCN & 0x80)); OSCICN = 0x08; // Configure timer for 22.1184 Mhz SYSCLK // 1000 ticks per second external_clock = true; for(;;) P3 = ((clock() / 1000) & 0x01) << 3; }
ec2writeflash is part of ec2drv once written by Ricky White. Since ec2drv is no longer maintained, we use the version from the ec2-new fork.
SDCC was initially written by Sandeep Dutta for the MCS-51, and has a relatively conservative architecture (see Sandeep Dutta, "Anatomy of a Compiler", 2000). It has been extended by various contributors and more recently, incorporated some cutting-edge technologies, in particular in register allocation (see Philipp Klaus Krause, "Optimal Register Allocation in Polynomial Time", 2013 and "Bytewise Register Allocation", 2015). However the mcs51 backend does not have all the fancy features and optimizations that some newer backends have.
SDCC is a C compiler that aims to be compliant with the C standards.
Important compiler options for MCS-51 developers include:
-c
to compile into object files to be linked later--std-c99
for compilation in C99 mode (some C99 features, e.g. variable-length arrays are not yet supported in sdcc though)--model-large
to use the xram for variables by default